// ----------------------------------------------------------------------------------
// Microsoft Developer & Platform Evangelism
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// ----------------------------------------------------------------------------------

using System;
using System.IdentityModel.Tokens;
using System.IO;
using System.Web;
using System.Xml;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Web;

namespace SL.IdentityModel.Server
{
    /// <summary>
    /// Class procesesses an incomming security token XML using token handlers specified in microsoft.identitymodel config
    /// also the class manages signin using a session cookie, as well as sign out
    /// </summary>
    public class TokenProcessor
    {
        /// <summary>
        /// Creates an instance of the class
        /// </summary>
        public TokenProcessor()
        { }

        /// <summary>
        /// Given tokenXML, the token is validated and the resulting IClaimsPrincipal is returned
        /// </summary>
        /// <param name="tokenXml">xml of security token</param>
        /// <returns>IClaimsPrincipal created from the token</returns>
        public IClaimsPrincipal Authenticate(string tokenXml)
        {
            if (String.IsNullOrEmpty(tokenXml))
            {
                throw new ArgumentException("tokenXML cannot be null or empty string");
            }

            SecurityToken token = ReadXmlToken(tokenXml);
            IClaimsPrincipal principal = AuthenticateSecurityToken(HttpContext.Current.Request.ApplicationPath, token);
            return principal;
        }

        /// <summary>
        /// Takes a IClaimsPrincipal and sets the session cookie
        /// </summary>
        /// <param name="principal">identity of user to be set in the cookie</param>
        public void CreateLoginSession(IClaimsPrincipal principal)
        {
            WSFederationAuthenticationModule activeModule = new WSFederationAuthenticationModule();
            activeModule.SetPrincipalAndWriteSessionToken(
                                        new SessionSecurityToken(principal, GetSessionLifetime()),
                                        true
                                        );
        }

        /// <summary>
        /// Clears the session cookie created by CreateLoginSession()
        /// </summary>
        public static void SignOut()
        {
            WSFederationAuthenticationModule federationAuthenticationModule = new WSFederationAuthenticationModule();
            federationAuthenticationModule.SignOut(false);
        }

        /// <summary>
        /// Security Token Handlers read from microsoft.identitymodel config
        /// </summary>
        private SecurityTokenHandlerCollection Handlers
        {
            get
            {
                return FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
            }
        }

        /// <summary>
        /// creates a security token from XML
        /// </summary>
        /// <param name="tokenXml">security token xml</param>
        /// <returns>security token</returns>
        private SecurityToken ReadXmlToken(string tokenXml)
        {

            using (StringReader strReader = new StringReader(tokenXml))
            {
                using (XmlDictionaryReader reader = XmlDictionaryReader.CreateDictionaryReader(XmlReader.Create(strReader)))
                {
                    reader.MoveToContent();
                    return Handlers.ReadToken(reader);
                }
            }
        }

        /// <summary>
        /// Calls token handler to authenticate a security token
        /// </summary>
        /// <param name="endpoint">endpoint to validate token against</param>
        /// <param name="token">security token</param>
        /// <returns></returns>
        private IClaimsPrincipal AuthenticateSecurityToken(string endpoint, SecurityToken token)
        {
            ClaimsIdentityCollection claims = Handlers.ValidateToken(token);
            IClaimsPrincipal principal = ClaimsPrincipal.CreateFromIdentities(claims);
            return FederatedAuthentication.ServiceConfiguration.ClaimsAuthenticationManager.Authenticate(endpoint, principal);
        }

        /// <summary>
        /// gets session lifetime form configured security token handler
        /// </summary>
        /// <returns></returns>
        private TimeSpan GetSessionLifetime()
        {
            TimeSpan lifetime = SessionSecurityTokenHandler.DefaultTokenLifetime;

            if (FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers != null)
            {
                SessionSecurityTokenHandler ssth = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers[typeof(SessionSecurityToken)] as SessionSecurityTokenHandler;
                if (ssth != null)
                {
                    lifetime = ssth.TokenLifetime;
                }
            }
            return lifetime;
        }
    }
}